package handler

import (
	"crypto/sha512"
	"encoding/hex"
	"github.com/gin-gonic/gin"
	"github.com/spf13/viper"
	"io"
	"net/http"
	"os"
	"strings"
	"time"
	"usercenter/internal/cache"
	"usercenter/internal/discovery"
	"usercenter/internal/model"
	"usercenter/internal/service"
	"usercenter/utils/errx"
	"usercenter/utils/logx"
	"usercenter/utils/resx"
	"usercenter/utils/secret"
)

// 统一鉴权通道

func Auth(ctx *gin.Context) {
	// 获取用户验证等级
	level := ctx.Param("level")

	// 获取 jwt
	jwtToken := ctx.Request.Header.Get("Authorization")

	// 验证缓存 token
	str := "user"
	if level == "sys" {
		str = "system"
	}
	state, err := cache.RedisDB.Get(str + ":token:" + jwtToken).Result()
	if err != nil {
		ctx.JSON(401, nil)
		return
	}
	if state != "login" {
		ctx.JSON(401, nil)
		return
	}

	// 验证 jwt 签名
	if !secret.VerifyJwtToken(jwtToken, viper.GetString("server.jwtSecret"), level) {
		ctx.JSON(401, nil)
		return
	}

	ctx.JSON(200, nil)
	return
}

// 公开组

func LoginByPwd(ctx *gin.Context) {
	// 获取请求信息
	var data LoginByPwdRequest
	ctx.BindJSON(&data)

	// 验证邮箱是否注册
	userInfo := model.QueryUserByEmail(data.Email)
	if userInfo.UserId == 0 {
		ctx.JSON(200, resx.GinResp(resx.Fail, "邮箱未注册"))
		return
	}

	// 验证密码是否正确
	if !secret.PwdVerify(data.Pwd, userInfo.Password) {
		ctx.JSON(200, resx.GinResp(resx.Fail, "密码错误"))
		return
	}

	// 生成 JwtToken
	uuid := secret.GenerateUUID()
	// TODO 配置化
	seconds := time.Hour
	// TODO 配置化
	jwtToken, err := secret.GetJwtToken(viper.GetString("server.jwtSecret"), uuid, userInfo.Email, userInfo.Nickname, userInfo.UserId, seconds, "def")
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}
	// 写入缓存
	// TODO 配置化
	cache.RedisDB.Set("user:token:"+jwtToken, "login", seconds)

	ctx.JSON(200, resx.GinResp(resx.Success, jwtToken))
	return
}

func LoginByCaptcha(ctx *gin.Context) {
	// 获取请求信息
	var data LoginByCaptchaRequest
	ctx.BindJSON(&data)

	// 验证邮箱是否注册
	userInfo := model.QueryUserByEmail(data.Email)
	if userInfo.UserId == 0 {
		ctx.JSON(200, resx.GinResp(resx.Fail, "邮箱未注册"))
		return
	}

	// TODO 读取配置
	// 生成 JwtToken
	uuid := secret.GenerateUUID()
	seconds := time.Hour
	jwtToken, err := secret.GetJwtToken(viper.GetString("server.jwtSecret"), uuid, userInfo.Email, userInfo.Nickname, userInfo.UserId, seconds, "def")
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}
	// 写入缓存
	cache.RedisDB.Set("user:token:"+jwtToken, "login", seconds)

	ctx.JSON(200, resx.GinResp(resx.Success, jwtToken))
	return
}

func Register(ctx *gin.Context) {
	// 获取请求信息
	var data map[string]interface{}
	var err error
	ctx.BindJSON(&data)
	if data["email"] == nil || data["email"] == "" || data["pwd"] == nil || data["pwd"] == "" {
		ctx.JSON(200, resx.GinResp(resx.BadRequest, nil))
		return
	}
	data["pwd"], err = secret.PwdHash(data["pwd"].(string))
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}
	if data["nickname"] == nil {
		data["nickname"] = data["email"]
	}

	// 添加 UUID-Operate 缓存记录： 邮箱验证 Step 1
	data["operate"] = "register"
	uuid := secret.GenerateUUID()
	cache.RedisDB.HMSet("user:link:"+uuid, data)
	// TODO 配置化
	cache.RedisDB.Expire("user:link:"+uuid, 3*time.Minute)

	// 发送邮箱链接验证
	//emailServiceClient := ctx.Keys["email"].(service.EmailServiceClient)
	//rpcResp, err := emailServiceClient.EmailGenerateLink(ctx, &service.EmailLinkGenerateRequest{
	//	Email:     data["email"].(string),
	//	Recipient: data["nickname"].(string),
	//	Uuid:      uuid,
	//})
	emailConn := discovery.EmailDiscover.Get()
	if emailConn == nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, errx.CtpError(errx.RpcError)))
		return
	}
	emailServiceClient := service.NewEmailServiceClient(emailConn)
	rpcResp, err := emailServiceClient.EmailGenerateLink(ctx, &service.EmailLinkGenerateRequest{
		Email:     data["email"].(string),
		Recipient: data["nickname"].(string),
		Uuid:      uuid,
	})
	if rpcResp.GetCode() == 14 {
		discovery.EmailDiscover.Sad(emailConn.Target())
	}

	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}
	if rpcResp.Code != 1200 {
		ctx.JSON(200, resx.GinResp(resx.Fail, rpcResp.Code))
		return
	}

	// 返回发送结果
	ctx.JSON(200, resx.GinResp(resx.Success, nil))
	return
}

// 鉴权组

func Info(ctx *gin.Context) {
	// 识别用户身份
	claims, err := secret.GetJwtClaims(ctx.Request.Header.Get("Authorization"))
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}

	// 获取用户数据
	userInfo := model.QueryUserById(claims.UserId)
	// 屏蔽密码
	userInfo.Password = ""

	ctx.JSON(200, resx.GinResp(resx.Success, userInfo))
	return
}

func Edit(ctx *gin.Context) {
	// 获取请求信息
	data := EditRequest{}
	ctx.BindJSON(&data)
	claims, err := secret.GetJwtClaims(ctx.Request.Header.Get("Authorization"))
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}
	if data.Nickname == "" {
		data.Nickname = claims.Email
	}

	// 更新用户信息
	err = model.UpdateUserByUserId(&model.User{
		Nickname:         data.Nickname,
		Mobile:           &data.Mobile,
		UserId:           claims.UserId,
		ProfilePhotoHash: data.ProfilePhotoHash,
	})
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}

	ctx.JSON(200, resx.GinResp(resx.Success, nil))
	return
}

func EditPwd(ctx *gin.Context) {
	// 获取请求信息
	data := EditPwdRequest{}
	ctx.BindJSON(&data)
	claims, err := secret.GetJwtClaims(ctx.Request.Header.Get("Authorization"))
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}

	// 加密密码
	pwd, err := secret.PwdHash(data.Pwd)
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}

	// 更新用户信息
	err = model.UpdateUserByUserId(&model.User{
		Password: pwd,
		UserId:   claims.UserId,
	})
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}

	ctx.JSON(200, resx.GinResp(resx.Success, nil))
	return
}

func ForgetPwd(ctx *gin.Context) {
	// 获取请求信息
	data := ForgetPwdRequest{}
	ctx.BindJSON(&data)

	// 加密密码
	pwd, err := secret.PwdHash(data.Pwd)
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}

	// 更新用户信息
	err = model.UpdateUserByUserEmail(&model.User{
		Password: pwd,
		Email:    data.Email,
	})
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}
	ctx.JSON(200, resx.GinResp(resx.Success, nil))
	return
}

func Logout(ctx *gin.Context) {
	jwtToken := ctx.Request.Header.Get("Authorization")
	// 关闭缓存令牌
	if logoutErr := cache.RedisDB.Del("user:token:" + jwtToken).Err(); logoutErr != nil {
		logx.LogrusObj.Warning(logoutErr)
	}

	ctx.JSON(200, resx.GinResp(resx.Success, nil))
	return
}

func Unregister(ctx *gin.Context) {
	jwtToken := ctx.Request.Header.Get("Authorization")
	claims, err := secret.GetJwtClaims(jwtToken)
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}

	// 软删除用户
	err = model.DeleteUser(claims.UserId)
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}

	// 关闭缓存令牌
	if logoutErr := cache.RedisDB.Del("user:token:" + jwtToken).Err(); logoutErr != nil {
		logx.LogrusObj.Warning(logoutErr)
	}

	ctx.JSON(200, resx.GinResp(resx.Success, nil))
	return
}

func Upload(ctx *gin.Context) {
	fileHeader, err := ctx.FormFile("upload")
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.BadRequest, nil))
		return
	}

	// 大小限制
	if fileHeader.Size > 1024*1024 {
		ctx.JSON(200, resx.GinResp(resx.SizeLimitExceeded, nil))
		return
	}

	// 文件类型判断以及计算哈希值
	file, err := fileHeader.Open()
	defer file.Close()
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}

	var data []byte
	buf := make([]byte, 10240)
	for {
		n, err := file.Read(buf)
		if err != nil && err != io.EOF {
			ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
			return
		}
		if n == 0 {
			break
		}
		data = append(data, buf[:n]...)
	}
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}

	bytes := sha512.Sum512(data)
	hex := hex.EncodeToString(bytes[:])
	path := "./static/image/" + hex

	if _, err := os.Stat(path); err != nil {
		fileType := http.DetectContentType(data[0:512])
		types := strings.Split(fileType, "/")
		if types[0] != "image" {
			ctx.JSON(200, resx.GinResp(resx.TypeNotAllowed, nil))
			return
		}
		saveErr := ctx.SaveUploadedFile(fileHeader, path)
		if saveErr != nil {
			ctx.JSON(200, resx.GinResp(resx.SystemErr, saveErr))
			return
		}
	}

	claims, err := secret.GetJwtClaims(ctx.Request.Header.Get("Authorization"))
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}
	userInfo := model.QueryUserById(claims.UserId)

	// 数据库更新
	model.UpdateUserPhoto(claims.UserId, hex)
	err = model.AddProfilePhoto(hex)
	if err != nil {
		logx.LogrusObj.Warning("[GORM] Inc profile photo fail", err)
	}

	if userInfo.ProfilePhotoHash != "" {
		err := model.DecProfilePhoto(userInfo.ProfilePhotoHash)
		if err != nil {
			if err == errx.CtpError(errx.DecError) {
				osErr := os.Remove("./static/image/" + userInfo.ProfilePhotoHash)
				if osErr != nil {
					logx.LogrusObj.Warning("[SYSTEM] Remove profile photo fail", osErr)
				}
			} else {
				logx.LogrusObj.Warning("[GORM] Dec profile photo fail", err)
			}
		}
	}

	ctx.JSON(200, resx.GinResp(resx.Success, hex))
}

/* ===================== 请求格式 ===================== */

type LoginByPwdRequest struct {
	Email string `json:"email" binding:"required"`
	Pwd   string `json:"pwd" binding:"required"`
}

type LoginByCaptchaRequest struct {
	Email string `json:"email" binding:"required"`
}

//type RegisterRequest struct {
//	Email    string `json:"email" binding:"required"`
//	Nickname string `json:"nickname" binding:"required"`
//	Mobile   string `json:"mobile"`
//	Pwd      string `json:"pwd" binding:"required"`
//}

type EditRequest struct {
	Nickname         string `json:"nickname" binding:"required"`
	Mobile           string `json:"mobile"`
	ProfilePhotoHash string `json:"profilePhotoHash" binding:"required"`
}

type EditPwdRequest struct {
	Pwd string `json:"pwd" binding:"required"`
}

type ForgetPwdRequest struct {
	Pwd   string `json:"pwd" binding:"required"`
	Email string `json:"email" binding:"required"`
}

// ======================================== 管理员 ========================================

// 公开组

func SysLogin(ctx *gin.Context) {
	// 获取请求信息
	var data LoginByPwdRequest
	ctx.BindJSON(&data)

	// 验证邮箱是否注册
	userInfo := model.QueryUserSysByEmail(data.Email)
	if userInfo.UserSysId == 0 {
		ctx.JSON(200, resx.GinResp(resx.Fail, "邮箱未注册"))
		return
	}

	// 验证密码是否正确
	if !secret.PwdVerify(data.Pwd, userInfo.Password) {
		ctx.JSON(200, resx.GinResp(resx.Fail, "密码错误"))
		return
	}

	// 生成 JwtToken
	uuid := secret.GenerateUUID()
	// TODO 配置化
	seconds := time.Hour
	// TODO 配置化
	jwtToken, err := secret.GetJwtToken(viper.GetString("server.jwtSecret"), uuid, userInfo.Email, userInfo.Nickname, userInfo.UserSysId, seconds, "sys")
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}
	// 写入缓存
	// TODO 配置化
	cache.RedisDB.Set("system:token:"+jwtToken, "login", seconds)

	ctx.JSON(200, resx.GinResp(resx.Success, jwtToken))
	return
}

func SysLogout(ctx *gin.Context) {
	jwtToken := ctx.Request.Header.Get("Authorization")
	// 关闭缓存令牌
	if logoutErr := cache.RedisDB.Del("system:token:" + jwtToken).Err(); logoutErr != nil {
		logx.LogrusObj.Warning(logoutErr)
	}

	ctx.JSON(200, resx.GinResp(resx.Success, nil))
	return
}
